Libraries and Packages in VHDL

Built-in Libraries and Packages.

In most vhdl programs you have already seen examples of packages and libraries. Here are two:

library ieee;

use ieee.std_logic_1164.all;

use ieee.std_logic_signed.all;

The packages are "std_logic_1164" and "std_logic_signed" and the library is "ieee". Since the "scope" of the library statement extends over the entire file, it is not necessary to repeat that for the second package.

It's instructive to show where the packages are physically located. For Altera Max+2 and Xilinx Foundation these locations typically are:

Altera: ~\maxplus2\vhdl93\ieee\std1164.vhd

Xilinx: ~\fndtn\synth\lib\packages\ieee\src\std_logic_1164.vhd

It is thus tempting to come to the conclusion that the "library ieee;" statement indicates the "directory" in which the std_logic_1164 package is located. Note, however, where it is in Synplicity:

Synplicty: ~\synplcty\LIB\vhd\std1164.vhd

In the latter there is no mention of "ieee" at all. It is thus more appropriate to think of "ieee" as a pointer to the location of the package. The directory structure shown in those three examples depicts the directories where the packages are loaded when the software is installed. The pointer "ieee" is hardcoded in the compilers and thus there is no need for the user to associate that pointer with the directory structure, nor is it possible to put the packages anywhere else after the software has been loaded.

The files "std1164.vhd" (Altera and Synplicty) and "std_logic_1164.vhd" (Xilinx) show a close resemblance to the package name as given in the "use" statement, but apparently they don't have to be the same as the package name. The actual package is in those files and shows up in there as the following statement:

package std_logic_1164 is

which is identical for all three .vhd files. That statement is reminiscent of the entity statement and indeed, a package is also considered a design unit, similar to an entity and an architecture.

It's in the "st_logic_1164" package that std_logic values, such as ‘1’, ‘0’, ‘Z’, etc. are defined. Open up one of those .vhd files with any ascii editor, and see how that is done. Make sure, however, not to make any changes in these files. So it'd be better to first copy them to a file with another name and look at that one, just to be on the safe side.

In addition to a "package", the file containing the package may also have a "package body", which is yet another design entity in the VHDL sense. An example is the definition of the "+" (addition) operator for use with std_logic_vectors. The language only defines "+" for integers and reals, thus a package in a library is needed to "overload", as it is called, the "+" operator so it can be applied to other things, e.g. std_logic_vector. The package std_logic_signed contains the definition of "+" for that case and the package body defines its functionality. Thus a package and a package body go together similarly to an entity and its architecture. However, a package may or may not have a package body, unlike an entity, which must have an architecture. Here is an example how that is arranged in the file containing the package std_logic_signed (in the file "signed.vhd" in both Altera and Synplicity and in the file "std_logic_signed.vhd" in Xilinx). The pertinent excerpt from that file is shown below:

package STD_LOGIC_SIGNED is

function "+"(L: STD_LOGIC_VECTOR; R: STD_LOGIC_VECTOR) return

STD_LOGIC_VECTOR;

-- other similar function definitions

end STD_LOGIC_SIGNED;

package body STD_LOGIC_SIGNED is

function "+"(L: STD_LOGIC_VECTOR; R: STD_LOGIC_VECTOR) return STD_LOGIC_VECTOR is

constant length: INTEGER := maximum(L'length, R'length);

variable result : STD_LOGIC_VECTOR (length-1 downto 0);

begin

result := SIGNED(L) + SIGNED(R); return std_logic_vector(result);

end;

-- other similar function descriptions

end STD_LOGIC_SIGNED;

The "L" and "R" stand for the left and right operand respectively, on either side of the operator "+". The "return" indicates the type of signal that results from the operation. The use of the "maximum" function allows the two operands to be of different length and makes the resultant signal width equal to the widest of the two vectors. Note, the function "maximum" must also be defined in the package body, as it is not an inherent part of the language. Note also that the .vhd file that contains a package need not be named the same as the package.

Constants, variables, types and subtypes are defined similarly in the various packages that come with the software. However, the contents of these packages is not identical and which items are defined where, may also differ from manufacturer to manufacturer. It is unfortunate that the standardization of the language does not extend to the contents of the packages or even their names. This may make the code non-portable. On the other hand, the concept of the "package" is powerful, especially since there is also a mechanism for setting up user libraries and packages, which is shown in the following example.

User Libraries and Packages.

User libraries and packages are setup very similarly to the built-in ones. However, in that case, the user is responsible for the directory structure, the contents of the files, etc. Note that the user must then also set up the pointer to the package. The following shows a complete example of this arrangement. There are two ways to do this: 1) with the "work" directory; 2) with a user library.

With the "work" library (Max+2-specific)

First we define the "work" library . This is the pointer to the working directory, i.e. where all the design files are kept and the software "knows" which one that is, as it is set up by the project definition. Thus if the user were to put a .vhd file, containing a package, in the current working directory, the statement referencing that package would be:

library work; -- not needed, but OK to include.

work.usr_package_name.all;

Note that the first statement may be omitted, since the pointer "work" is built into the software. It is always the current working directory. An example of this structure follows:

myxor3.vhd

library ieee;

use ieee.std_logic_1164.all;

use work.my_package.all;

entity myxor3 is

port(a,b,c:in std_logic; x:out std_logic);

end entity myxor3;

architecture struct of myxor3 is

signal x_i:std_logic;

begin

u0: myxor port map(a,b,x_i);

u1: myxor port map(x_i,c,x);

end architecture struct;

There is no mention of a "component" myxor, because "myxor" is defined in the package "my_gates" and the compiler is told where that can be found through the "use.work..." statement. The file containing that package may be as follows:

--my_gates.vhd in the "work" directory (in this example)

library ieee;

use ieee.std_logic_1164.all;

package my_package is

component myxor is

port(p,q:in std_logic; f:out std_logic);

end component myxor;

end package my_package;

The component "myxor" is defined in the file myxor.vhd in the "work" directory, as shown below:

--myxor.vhd in the "work" directory

library ieee;

use ieee.std_logic_1164.all;

entity myxor is

port(a,b:in std_logic; f:out std_logic);

end entity myxor;

architecture a_myxor of myxor is

begin

f<=a xor b;

end archicture a_myxor;

Assuming the working directory is "mywork", then the structure is as follows:

~\max2work\mywork\myxor3.vhd

~\max2work\mywork\mygates.vhd

~\max2work\mywork\myxor.vhd

Although the package file "mygates.vhd" may contain several component definitions, each one must have it's own ".vhd" file that contains the entity/architecture pair that describes that component. At least those are restrictions imposed by the Altera software.

The main drawback of this simple package arrangement is that the package(s) can only be accessed by the designer(s) who are using the same working directory. When multiple designers work on a project, it is desirable for each of them to have their own working directory and have access to a common user library. This is shown in the following example.

With a "user" directory.

This example uses Synplicity for the compiler, since it does not impose the restrictions of the Altera Max+2 compiler, but behaves as the language intends. Since the result of this compilation is an EDIF file, it can however, be exported to Max+2 and from then on used as any internal VHDL source file for simulation and implementation in an Altera FPGA.

Assuming the common user library is "usrlib01" (the "01" is to emphasize the fact that there my be several user libraries), the directory structure may be as follows.

~\max2work\usr_a\...usr_a's .vhd files...

~\max2work\usr_b\...usr_b's .vhd files...

more team member's files

~\max2work\usrlib01\...package file...

There are four main differences with the previous, simpler, Altera-based, example:

  1. There now must be a library statement identifying the package library.

  2. The designer must decide on a name for the pointer to the package.

  3. Before compilation, the pointer must be associated with the package in "usrlib01".

  4. There are no component .vhd files, since the components are completely defined within the package file (not possible in Max+2).

A complete example is given below, including the commands needed for the library (pointer) setup. This example is a little more elaborate than the previous one as it shows the use of multiple components, which is more typical of a large design.

To clarify the structure, the code represents the circuit shown below:

 

We'll use a structural or hierarchical approach in the VHDL code, i.e. the architecture portion contains references to components MYAND2 and MYOR2. The entity/architecture pairs for these components will be described in a package file. The design requires a source file for the circuit, a package file, a package name, a pointer to the package, a working directory and a user library. It is possible to use the same name for some of these items, but here we'll use all different ones to make it clearer what is what. A list of the various constituents is shown on the next page

  1. cct.vhd -- the source file for the circuit shown on the previous page.

  2. p_gates.vhd -- the file containing the package.

  3. gates -- the name of the package in p_gates.vhd.

  4. ~\usr_a\ -- the working directory of usr_a.

  5. ~\usrlib01\ -- the directory containing p_gates.vhd.

  6. gateslib -- the name of the pointer to the package gates.

It doesn't matter in which order cct.vhd and p_gates.vhd are written and compiled, but it is instructive to do "cct.vhd" first, just to see what the Synplicty compiler does with a source file when the descriptions of the components referenced therein are missing.

The code for cct.vhd is as follows:

--cct.vhd in ~\usr_a\

--component descriptions are in the package "gates", which is in

--the file ~\usrlib01\p_gates.vhd;

--the pointer is "gateslib"

library ieee;

use ieee.std_logic_1164.all;

library gateslib;

use gateslib.gates.all;

entity cct is --the file name may be different from the entity name.

port(a,b,c:in std_logic;

x:out std_logic);

end entity cct;

architecture a_cct of cct is

signal x_i:std_logic;

begin

u0: myand2 port map(a,b,x_i);

u1: myor2 port map(x_i,c,x);

end architecture a_cct;

Note that it is desirable to list the directory where the package is located in the comment section, since it doesn't appear in the code itself. Note also, that in the code there is no mention of a component.

When you first write this code, leave out or "comment out" the statements:

library gateslib;

use gateslib.gates.all;

and then compile it after you have "added" cct.vhd to the project. The reason is to see what warning/error message(s) that causes. One of them is: "Undefined identifier" with myand2 highlighted in the source code. It does mapping though and the RTL view is as shown below:

This looks OK, because it's just like the schematic shown on page 1. However, keep in mind that the source code by itself contains enough information to draw an entity. What you cannot see is that the blocks are empty.

Now re-instate the user library statements and compile cct.vhd again. This time the compiler will give several errors, one of them being:

"Can't find library gateslib"

That is not surprising, since the package file "p_gates.vhd" doesn't even exist yet. However, you would get the same message if the file did exist but it hadn't been added to the project or no pointer had been defined.

Here is the code for the package file:

--p_gates.vhd in ~\usrlib01\

library ieee;

use ieee.std_logic_1164.all;

package gates is

component myand2 is

port(a,b:in std_logic;

x:out std_logic);

end component myand2;

component myor2 is

port(a,b:in std_logic;

x:out std_logic);

end component myor2;

end package gates; continued on next page

use ieee.std_logic_1164.all; continued from previous page

entity myand2 is

port(a,b:in std_logic;

x:out std_logic);

end entity myand2;

architecture a_myand2 of myand2 is

begin

x<=a and b;

end architecture a_myand2;

use ieee.std_logic_1164.all;

entity myor2 is

port(a,b:in std_logic;

x:out std_logic);

end entity myor2;

architecture a_myor2 of myor2 is

begin

x<=a or b;

end architecture a_myor2;

There are several things worth noting. One; the "use ieee..." statement must be repeated before every entity/architecture pair that needs it, since the "scope" of that statement extends only over the immediately following entity/architecture pair. On the other hand, the "library ieee..." statement need not be repeated, because its scope covers the entire file. For easy of reading and documentation purposes, it may be repeated, however. Two; there are two entity/architecture pairs described in this file, which would be impossible in Max+2, which makes the use of packages impractical in that software. The way Synplicty handles packages is the way it is intended in the language.

Now add p_gates.vhd to the project, remove all other source files and compile it.The reason for deleting all other .vhd files is so that any warnings/errors pertain only to the package file. After having corrected all warnings/errors, add the original source file cct.vhd to the project and compile it again. The error message is again "Can’t find library gateslib", although the package is in the right place and compiled correctly. However, the pointer to that package has not yet been defined. Select p_gates in the source file box and "Set Library" from the "Project" menu. In the file properties window that opens, you’ll see the path to the package file. In the library slot enter "gateslib" and select OK. That window will then look as follows:

 

In Synplicty’s main window the location of p_gates will change from [work] to [gateslib] as shown below:

NOTE: The .vhd files must be in the order shown above!!!

Multiple packages and libraries.

In the case of large designs, it is not unusual for the design department to obtain packages from third parties, so they don't have to do all the development work for components, for instance, themselves. However, it may then happen that components with different functionality in different packages have the same name. If those packages are used with the usual "use library.package_name.all" statement, it causes a conflict, as the compiler cannot tell which component is meant to be used. There are two ways to avoid this:

  1. Instead of the previous "use" statement, change it to:

    "use library_1.package_1.component_1;

    "use library_1.package_2.component_2;

    This way only those two components are made "visible" in the design file. If many components are to be used, it becomes a little wordy, but the conflict is avoided.

  2. Put only the statement "library library_1, library_2, .....; in the design file, but no "use" statement. In the architecture section, where the components are instantiated, instead of the usual "u0: component_1 port map(...............);" write the following:

"u0: entity library_1.package_1.component_1 port map(....................);"

This has the advantage that right where the instantiation is done, it is very clear where that component is defined.

Although both methods are OK so far as the language is concerned, the first one cannot be used in Synplicity, at least not for the time being. This is a bug in their software and they have assured me, it will be addressed. However, the second method works fine.

The following simple example shows the top level code for such an arrangement.

--topcct.vhd in ~\hvdb\

library ieee;

use ieee.std_logic_1164.all;

library pg1,pg2;

entity topcct is

port(p,q,r:in std_logic; f:out std_logic);

end entity topcct;

architecture a_topcct of topcct is

signal x_i: std_logic;

begin

u0: entity pgt2.gates2.myand port map(p,q,x_i);

u1: entity pgt1.gates1.myor port map(x_i,r,f);

end architecture a_topcct;

Two packages are used, with two components each. Package_1 has a dual-input AND and a dual-input OR gate. Package_2 has a dual-input AND and a triple-input OR gate. They are named the same, however, and thus there would be a conflict because of the OR gates, if the ".all" statement were used. The packages are in files p_gates1.vhd and p_gates2.vhd respectively, both in the directory usrlib01, which is not the working directory. The complete code for both packages is shown below:

--p_gates1.vhd in ~\usrlib01\

library ieee;

use ieee.std_logic_1164.all;

package gates1 is

component myand is

port(a,b: in std_logic;

x: out std_logic);

end component myand;

component myor is

port(a,b: in std_logic;

x: out std_logic);

end component myor;

end package gates1;

use ieee.std_logic_1164.all;

entity myand is

port(a,b: in std_logic;

x: out std_logic);

end entity myand;

architecture a_myand of myand is

begin

x<=a and b;

end architecture a_myand;

use ieee.std_logic_1164.all;

entity myor is

port(a,b: in std_logic;

x: out std_logic);

end entity myor;

architecture a_myor of myor is

begin

x<=a or b;

end architecture a_myor;

 

--p_gates2.vhd in ~\usrlib01\

library ieee;

use ieee.std_logic_1164.all;

package gates2 is

component myand is

port(a,b: in std_logic;

x: out std_logic);

end component myand;

component myor is

port(a,b,c: in std_logic;

x: out std_logic);

end component myor;

end package gates2;

use ieee.std_logic_1164.all;

entity myand is

port(a,b: in std_logic;

x: out std_logic);

end entity myand;

architecture a_myand of myand is

begin

x<=a and b;

end architecture a_myand;

use ieee.std_logic_1164.all;

entity myor is

port(a,b,c: in std_logic;

x: out std_logic);

end entity myor;

architecture a_myor of myor is

begin

x<=a or b or c;

end architecture a_myor;

From here on the procedure is as explained for the single-package example.

After compilation, the project window should look as shown below:

 

 

The RTL view, shown below, is as expected and depicts the circuit described in topcct.vhd